{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# AWS Marketplace Product Usage Demonstration - Algorithms\n",
    "\n",
    "## Using Algorithm ARN with Amazon SageMaker APIs\n",
    "\n",
    "This sample notebook demonstrates two new functionalities added to Amazon SageMaker:\n",
    "1. Using an Algorithm ARN to run training jobs and use that result for inference\n",
    "2. Using an AWS Marketplace product ARN - we will use [Scikit Decision Trees](https://aws.amazon.com/marketplace/pp/prodview-ha4f3kqugba3u?qid=1543169069960&sr=0-1&ref_=srh_res_product_title)\n",
    "\n",
    "## Overall flow diagram\n",
    "<img src=\"images/AlgorithmE2EFlow.jpg\">\n",
    "\n",
    "## Compatibility\n",
    "This notebook is compatible only with [Scikit Decision Trees](https://aws.amazon.com/marketplace/pp/prodview-ha4f3kqugba3u?qid=1543169069960&sr=0-1&ref_=srh_res_product_title) sample algorithm published to AWS Marketplace. \n",
    "\n",
    "***Pre-Requisite:*** Please subscribe to this free product before proceeding with this notebook"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Set up the environment"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import sagemaker as sage\n",
    "from sagemaker import get_execution_role\n",
    "\n",
    "role = get_execution_role()\n",
    "\n",
    "# S3 prefixes\n",
    "common_prefix = \"DEMO-scikit-byo-iris\"\n",
    "training_input_prefix = common_prefix + \"/training-input-data\"\n",
    "batch_inference_input_prefix = common_prefix + \"/batch-inference-input-data\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create the session\n",
    "\n",
    "The session remembers our connection parameters to Amazon SageMaker. We'll use it to perform all of our Amazon SageMaker operations."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sagemaker_session = sage.Session()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Upload the data for training\n",
    "\n",
    "When training large models with huge amounts of data, you'll typically use big data tools, like Amazon Athena, AWS Glue, or Amazon EMR, to create your data in S3. For the purposes of this example, we're using some the classic [Iris dataset](https://en.wikipedia.org/wiki/Iris_flower_data_set), which we have included. \n",
    "\n",
    "We can use use the tools provided by the Amazon SageMaker Python SDK to upload the data to a default bucket. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "TRAINING_WORKDIR = \"data/training\"\n",
    "\n",
    "training_input = sagemaker_session.upload_data(TRAINING_WORKDIR, key_prefix=training_input_prefix)\n",
    "print (\"Training Data Location \" + training_input)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Creating Training Job using Algorithm ARN\n",
    "\n",
    "Please put in the algorithm arn you want to use below. This can either be an AWS Marketplace algorithm you subscribed to (or) one of the algorithms you created in your own account.\n",
    "\n",
    "The algorithm arn listed below belongs to the [Scikit Decision Trees](https://aws.amazon.com/marketplace/pp/prodview-ha4f3kqugba3u?qid=1543169069960&sr=0-1&ref_=srh_res_product_title) product."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from src.scikit_product_arns import ScikitArnProvider\n",
    "\n",
    "algorithm_arn = ScikitArnProvider.get_algorithm_arn(sagemaker_session.boto_region_name)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import json\n",
    "import time\n",
    "from sagemaker.algorithm import AlgorithmEstimator\n",
    "\n",
    "algo = AlgorithmEstimator(\n",
    "            algorithm_arn=algorithm_arn,\n",
    "            role=role,\n",
    "            train_instance_count=1,\n",
    "            train_instance_type='ml.c4.xlarge',\n",
    "            base_job_name='scikit-from-aws-marketplace')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Run Training Job"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print (\"Now run the training job using algorithm arn %s in region %s\" % (algorithm_arn, sagemaker_session.boto_region_name))\n",
    "algo.fit({'training': training_input})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Automated Model Tuning (optional)\n",
    "\n",
    "Since this algorithm supports tunable hyperparameters with a tuning objective metric, we can run a Hyperparameter Tuning Job to obtain the best training job hyperparameters and its corresponding model artifacts. \n",
    "\n",
    "<img src=\"images/HPOFlow.jpg\">\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sagemaker.tuner import HyperparameterTuner, IntegerParameter\n",
    "\n",
    "## This demo algorithm supports max_leaf_nodes as the only tunable hyperparameter.\n",
    "hyperparameter_ranges = {'max_leaf_nodes': IntegerParameter(1, 100000)}\n",
    "\n",
    "tuner = HyperparameterTuner(estimator=algo, base_tuning_job_name='some-name',\n",
    "                                objective_metric_name='validation:accuracy',\n",
    "                                hyperparameter_ranges=hyperparameter_ranges,\n",
    "                                max_jobs=2, max_parallel_jobs=2)\n",
    "\n",
    "tuner.fit({'training': training_input}, include_cls_metadata=False)\n",
    "tuner.wait()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Batch Transform Job\n",
    "\n",
    "Now let's use the model built to run a batch inference job and verify it works.\n",
    "\n",
    "### Batch Transform Input Preparation\n",
    "\n",
    "The snippet below is removing the \"label\" column (column indexed at 0) and retaining the rest to be batch transform's input. \n",
    "\n",
    "***NOTE:*** This is the same training data, which is a no-no from a ML science perspective. But the aim of this notebook is to demonstrate how things work end-to-end."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "\n",
    "## Remove first column that contains the label\n",
    "shape=pd.read_csv(TRAINING_WORKDIR + \"/iris.csv\", header=None).drop([0], axis=1)\n",
    "\n",
    "TRANSFORM_WORKDIR = \"data/transform\"\n",
    "shape.to_csv(TRANSFORM_WORKDIR + \"/batchtransform_test.csv\", index=False, header=False)\n",
    "\n",
    "transform_input = sagemaker_session.upload_data(TRANSFORM_WORKDIR, key_prefix=batch_inference_input_prefix) + \"/batchtransform_test.csv\"\n",
    "print(\"Transform input uploaded to \" + transform_input)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "transformer = algo.transformer(1, 'ml.m4.xlarge')\n",
    "transformer.transform(transform_input, content_type='text/csv')\n",
    "transformer.wait()\n",
    "\n",
    "print(\"Batch Transform output saved to \" + transformer.output_path)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Inspect the Batch Transform Output in S3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from urllib.parse import urlparse\n",
    "\n",
    "parsed_url = urlparse(transformer.output_path)\n",
    "bucket_name = parsed_url.netloc\n",
    "file_key = '{}/{}.out'.format(parsed_url.path[1:], \"batchtransform_test.csv\")\n",
    "\n",
    "s3_client = sagemaker_session.boto_session.client('s3')\n",
    "\n",
    "response = s3_client.get_object(Bucket = sagemaker_session.default_bucket(), Key = file_key)\n",
    "response_bytes = response['Body'].read().decode('utf-8')\n",
    "print(response_bytes)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Live Inference Endpoint\n",
    "\n",
    "Finally, we demonstrate the creation of an endpoint for live inference using this AWS Marketplace algorithm generated model\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sagemaker.predictor import csv_serializer\n",
    "predictor = algo.deploy(1, 'ml.m4.xlarge', serializer=csv_serializer)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Choose some data and use it for a prediction\n",
    "\n",
    "In order to do some predictions, we'll extract some of the data we used for training and do predictions against it. This is, of course, bad statistical practice, but a good way to see how the mechanism works.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "shape=pd.read_csv(TRAINING_WORKDIR + \"/iris.csv\", header=None)\n",
    "\n",
    "import itertools\n",
    "\n",
    "a = [50*i for i in range(3)]\n",
    "b = [40+i for i in range(10)]\n",
    "indices = [i+j for i,j in itertools.product(a,b)]\n",
    "\n",
    "test_data=shape.iloc[indices[:-1]]\n",
    "test_X=test_data.iloc[:,1:]\n",
    "test_y=test_data.iloc[:,0]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Prediction is as easy as calling predict with the predictor we got back from deploy and the data we want to do predictions with. The serializers take care of doing the data conversions for us."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(predictor.predict(test_X.values).decode('utf-8'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Cleanup the endpoint"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "algo.delete_endpoint()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "conda_python3",
   "language": "python",
   "name": "conda_python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
